package com.grj.spring;

import com.grj.spring.annotation.*;
import com.grj.spring.config.*;
import com.grj.spring.service.UserService;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 14505
 */

@ComponentScan("com.grj.spring")
public class MyAppcation {

    static Map<String,Object> beanDefinitionMap = new HashMap<>();
    static Map<String,Object> singletonObjectsMap = new HashMap<>();
    static List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public static void main(String[] args) throws Exception {
        ComponentScan annotation = MyAppcation.class.getAnnotation(ComponentScan.class);
        String packagePath = annotation.value().replace(".", "/");
        ClassLoader classLoader = MyAppcation.class.getClassLoader();
        URL resource = classLoader.getResource(packagePath);
        assert resource != null;
        File file = new File(resource.getPath());
        File[] files = file.listFiles();
        assert files != null;
        for (File f : files) {
            if (f.isDirectory()) {
                File[] files1 = f.listFiles();
                assert files1 != null;
                for (File f1 : files1) {
                    if (f1.getName().endsWith(".class")) {
                        String filePath = annotation.value()+"."+f.getName()+"."+f1.getName().replace(".class","");
                        Class<?> clazz = Class.forName(filePath);
                        if(clazz.isAnnotationPresent(Component.class)){
                            String f1name = f1.getName().replace(".class", "");
                            char[] chars = f1name.toCharArray();
                            String name = f1name.substring(0,1).toLowerCase() + f1name.substring(1,chars.length);
                            beanDefinitionMap.put(name, filePath);
                            if(BeanPostProcessor.class.isAssignableFrom(clazz)){
                                beanPostProcessorList.add((BeanPostProcessor) clazz.newInstance());
                            }
                        }
                    }
                }
            }
        }

        beanDefinitionMap.forEach((k,v) -> {
            try {
                Class<?> clazz = Class.forName(v.toString());
                Object o = clazz.newInstance();
                singletonObjectsMap.put(k, o);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

        beanDefinitionMap.forEach((k,v) -> {
            try {
                Class<?> clazz = Class.forName(v.toString());
                Object o = clazz.newInstance();
                Field[] declaredFields = clazz.getDeclaredFields();
                for (Field field : declaredFields) {
                    field.setAccessible(true);
                    if(field.isAnnotationPresent(Autowired.class)){
                        field.set(o, singletonObjectsMap.get(field.getName()));
                    }
                }

                Method[] declaredMethods = clazz.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {
                    declaredMethod.setAccessible(true);
                    if(declaredMethod.isAnnotationPresent(PostConstruct.class)){
                        declaredMethod.invoke(o);
                    }
                }

                for (Field field : declaredFields) {
                    field.setAccessible(true);
                    if(field.isAnnotationPresent(PostConstruct.class)){
                        field.set(o, singletonObjectsMap.get(field.getName()));
                    }
                    if(field.isAnnotationPresent(Select.class)){
                        field.set(o, singletonObjectsMap.get(field.getName()));
                    }
                }

                if(o instanceof BeanNameAware){
                    ((BeanNameAware)o).setBeanName(k);
                }

                for (BeanPostProcessor beanPostProcessor : beanPostProcessorList){
                    beanPostProcessor.postProcessBeforeInitialization(k, o);
                }

                if(o instanceof Initialization){
                    ((Initialization)o).afterPropertiesSet();
                }

                for (BeanPostProcessor beanPostProcessor : beanPostProcessorList){
                    beanPostProcessor.postProcessAfterInitialization(k, o);
                }

                singletonObjectsMap.put(k, o);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

//        singletonObjectsMap.forEach((k,v)->{
//            System.out.println(k+"="+v);
//        });

        UserService userService = (UserService) singletonObjectsMap.get("userService");
//        System.out.println(userService.sayHello());
        userService.down();
    }
}
